#!/bin/bash
#
#  Copyright 2013 Christopher Dudley
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

# This is a front-end script to the various datapath scripts.
# This script will be linked into /usr/local/bin during installation so that
# datapath can be run from any directory in the system.

##### Helper Functions #####

function printUsage {
    echo "Usage: datapath [options] <command> [file]"
    echo "Supported commands are:"
    echo "      run"
    echo "      debug"
    echo
    echo "Options:"
    echo "  -h              Show this help message"
    echo "  -o              Disable optimization of generated code"
    echo "  -n              Run non-interactively"
    echo "  -w              Disable read-only mode"
    echo "  -v              Enable verbose ouput"
}

function getFile {
    FILE=$(readlink -e $1)

    if [ $? != 0 ]; then
        echo "Error: File $1 not found."
        exit -1
    fi
}

# Returns 1 if yes and 0 if no
# $1 = Prompt
# $2 = variable in which to store result
function getYesOrNo {
    # Valid answers for yes and no
    local ANS_YES="y yes"
    local ANS_NO="n no"

    local __resultvar=$2

    while [ 0 ]; do
        read -p "$1" ans

        # Check if yes
        echo $ANS_YES | grep -i -w "$ans" &>/dev/null
        if [ $? == 0 ]; then
            local myresult=1
            eval $__resultvar="'$myresult'"
            return
        fi

        # Check if no
        echo $ANS_NO | grep -i -w "$ans" &>/dev/null
        if [ $? == 0 ]; then
            local myresult=0
            eval $__resultvar="'$myresult'"
            return
        fi

        # Didn't get either
        echo "Please answer y(es) or n(o)."
    done
}

function runTest {
    cd $DP_DIR

    if [ $NO_OPTS == true ]; then
        # Turn off optimizations
        export DEBUG_DATAPATH=true
    fi

    if [ $debug == 1 ]; then
        # Run through the debugger
        cat > gdb-commands <<EOF
run $DP_OPTS $1
EOF

        gdb --command=gdb-commands ./dp
    else
        # Just run normally
        ./dp $DP_OPTS $1
    fi
}

# $1 = List of paths (colon separated)
# $2 = subdirectory in those paths to check for
function getExistingSubpaths {
    local allPaths=""

    local prevIFS="$IFS"
    IFS=":"

    for path in "$1"; do
        cannonicalPath=$(readlink -e $path)
        if [ $? == 0 ]; then
            allPaths+="${IFS}${cannonicalPath}"
        fi
    done

    IFS="$prevIFS"

    echo "$allPaths"
}

function getPaths {
    # Set up Datapath installation paths

    if [ "x$DATAPATH_INSTALL_PATH" == "x" ]; then
        # DATAPATH_INSTALL_PATH is not set
        echo "Error: DataPath environment variables are not set."
        echo "If you have recently installed DataPath, please close your current terminal and"
        echo "start a new one. They environment variables are only set for terminals created"
        echo "after the installation of DataPath."
        exit -1
    else
        BASE_DIR=$DATAPATH_INSTALL_PATH
    fi

    DP_DIR=$DATAPATH_EXEC_PATH
    DP_LOCK_FILE=$DATAPATH_LOCK_FILE

    LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib:$(getExistingSubpaths "$DATAPATH_LIBRARY_PATH" "libraries")
    export LD_LIBRARY_PATH

    if [ $VERBOSE == 1 ]; then
        echo "Install Directory     : $BASE_DIR"
        echo "Executable Directory  : $DP_DIR"
        echo "Library path(s)       : $DATAPATH_LIBRARY_PATH"
        echo "Shared Libraries      : $DP_SHARED_LIB_DIR"
        echo "Lock File             : $DP_LOCK_FILE"
        echo "LD_LIBRARY_PATH       : $LD_LIBRARY_PATH"
    fi
}

function executeDatapath {
    getPaths

    if [ ! -e $DP_LOCK_FILE ]; then
        touch $DP_LOCK_FILE
        if (($? != 0)); then
            echo "Failed to create lock file at $DP_LOCK_FILE"
            echo "Please contact the system administrator."
            exit 1
        fi
    fi

    # Open the lock file
    exec 9<$DP_LOCK_FILE

    finished=0
    exitCode=0
    while (( $finished == 0 )); do
        # Try to lock the datapath-lock
        flock -n 9
        if (( $? != 0 )); then
            if (($INTERACTIVE == 0)); then
                # Just exit
                exitCode=1
                finished=1
                continue
            fi

            echo "Warning: A DataPath process is currently running."
            echo "Do you wish to terminate the currently running process?"

            getYesOrNo "Kill process? (y/n): " yesOrNoResult
            if [ ${yesOrNoResult} == 1 ]; then
                # Kill datapath and try to get the lock again
                gotAns=1
                echo "Killing currently running datapath instance."
                killall dp
                sleep 1
            else
                echo "Aborting."
                finished=1
                exitCode=1
            fi
        else
            # We got the lock
            finished=1
            runTest $1
        fi
    done

    # Close the lock file
    exec 9>&-
}

##### Argument Parsing #####

NO_OPTS=false
READ_ONLY=true
INTERACTIVE=1
VERBOSE=0

while getopts "honwv" opt; do
    case $opt in
        h)
            # Print usage and exit
            printUsage
            exit 0
            ;;
        o)
            NO_OPTS=true
            ;;
        n)
            INTERACTIVE=0
            ;;
        w)
            READ_ONLY=false
            ;;
        v)
            VERBOSE=1
            ;;
        \?)
            echo "Error: Unknown option -$OPTARG" >&2
            printUsage
            exit -1
            ;;
    esac
done

shift $(expr $OPTIND - 1)

if (( $# < 1 )); then
    echo "Error: No command specified."
    printUsage
    exit -1
fi

debug=0

DP_OPTS="-q"
if [ $READ_ONLY == true ]; then
    DP_OPTS="$DP_OPTS -r"
fi

DP_OPTS="$DP_OPTS -e"

case $1 in
    debug)
        debug=1
        ;&
    run)
        getFile $2
        executeDatapath $FILE
        ;;
    *)
        echo "Error: Unsupported command $1"
        printUsage
        exit -1
        ;;
esac

### End Argument Parsing ###

exit $exitCode
